[https://nvbugs/6388205][fix] Surface crashed trtllm-serve worker log on perf-sanity failures#15837
Conversation
📝 WalkthroughWalkthroughThis change adds crash triage logic to the perf sanity test module. Two helper functions detect abnormal subprocess exits and print server log tails via ChangesServer Crash Triage in Perf Sanity Tests
Estimated code review effort: 2 (Simple) | ~12 minutes Sequence Diagram(s)sequenceDiagram
participant TestRunner
participant ServerProc
participant Helpers
participant LogFile
TestRunner->>ServerProc: poll return code
TestRunner->>Helpers: _server_exited_abnormally(returncode)
Helpers-->>TestRunner: abnormal exit boolean
TestRunner->>ServerProc: terminate and wait
alt exception raised or abnormal exit
TestRunner->>Helpers: _echo_server_log_tail(log_path)
Helpers->>LogFile: read tail lines
Helpers->>TestRunner: print_error(log tail)
end
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
tests/integration/defs/perf/test_perf_sanity.py (2)
1006-1012: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winAvoid loading the whole log into memory to tail it.
readlines()[-num_lines:]materializes the entire file before slicing. A crashedtrtllm-serveworker can emit very large logs, so this can spike memory (or OOM) exactly during CI triage. Iterating with a boundeddequekeeps only the lastnum_linesin memory.♻️ Bounded tail read
+from collections import deque + ... try: - with open(log_path, "r", errors="replace") as log_file: - tail = "".join(log_file.readlines()[-num_lines:]) + with open(log_path, "r", errors="replace") as log_file: + tail = "".join(deque(log_file, maxlen=num_lines)) except OSError as err:(Place the
dequeimport with the other top-of-file imports.)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/integration/defs/perf/test_perf_sanity.py` around lines 1006 - 1012, The log tailing logic in the server log helper currently uses readlines()[-num_lines:], which loads the entire file into memory before slicing. Update the log-reading path to use a bounded deque for tailing instead, and add the deque import alongside the other top-level imports. Keep the existing OSError handling and print_error behavior in the same helper, but ensure only the last num_lines lines are retained in memory.
1116-1131: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
wait()has no timeout and SIGKILL escalation never happens.
server_proc.terminate()sends only SIGTERM, thenserver_proc.wait()blocks unbounded. If the worker ignores SIGTERM (common for a hung server that is exactly what you're trying to triage), cleanup hangs indefinitely and the log tail is never printed. Note_server_exited_abnormallyand its docstring already anticipate-SIGKILL, but nothing here escalates tokill().This same block is duplicated in the ctx/gen branch (Lines 1344-1354) and the
DISAGG_SERVERbranch (Lines 1379-1389). Extracting a single helper resolves the hang and the duplication together.♻️ Shared terminate-with-escalation helper
def _shutdown_and_maybe_echo(proc, log_path, label): raising = sys.exc_info()[0] is not None pre_rc = proc.poll() proc.terminate() try: rc = proc.wait(timeout=30) except subprocess.TimeoutExpired: proc.kill() rc = proc.wait() if raising or _server_exited_abnormally(pre_rc, rc): _echo_server_log_tail(log_path, label, pre_rc if pre_rc is not None else rc)Then each
finallyreduces to a guarded call, e.g.:finally: if server_proc: - raising = sys.exc_info()[0] is not None - pre_rc = server_proc.poll() - server_proc.terminate() - rc = server_proc.wait() - if raising or _server_exited_abnormally(pre_rc, rc): - _echo_server_log_tail( - server_file_path, - f"aggr:{server_idx}", - pre_rc if pre_rc is not None else rc, - ) + _shutdown_and_maybe_echo( + server_proc, server_file_path, f"aggr:{server_idx}" + )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/integration/defs/perf/test_perf_sanity.py` around lines 1116 - 1131, The shutdown path in the server cleanup block can hang indefinitely because `server_proc.wait()` has no timeout and never escalates past SIGTERM. Update the cleanup logic around `server_proc.terminate()` in this `finally` block, and in the duplicated ctx/gen and `DISAGG_SERVER` branches, to use a shared helper (for example, a `_shutdown_and_maybe_echo`-style routine) that waits with a timeout, calls `server_proc.kill()` on timeout, then waits again before deciding whether to call `_echo_server_log_tail`. Keep the existing `_server_exited_abnormally` check and reuse the current `pre_rc`/`rc` handling so the log tail still prints on failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/integration/defs/perf/test_perf_sanity.py`:
- Around line 1006-1012: The log tailing logic in the server log helper
currently uses readlines()[-num_lines:], which loads the entire file into memory
before slicing. Update the log-reading path to use a bounded deque for tailing
instead, and add the deque import alongside the other top-level imports. Keep
the existing OSError handling and print_error behavior in the same helper, but
ensure only the last num_lines lines are retained in memory.
- Around line 1116-1131: The shutdown path in the server cleanup block can hang
indefinitely because `server_proc.wait()` has no timeout and never escalates
past SIGTERM. Update the cleanup logic around `server_proc.terminate()` in this
`finally` block, and in the duplicated ctx/gen and `DISAGG_SERVER` branches, to
use a shared helper (for example, a `_shutdown_and_maybe_echo`-style routine)
that waits with a timeout, calls `server_proc.kill()` on timeout, then waits
again before deciding whether to call `_echo_server_log_tail`. Keep the existing
`_server_exited_abnormally` check and reuse the current `pre_rc`/`rc` handling
so the log tail still prints on failure.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: e27fc96f-d898-497c-bbcc-d4b7bf4dc79b
📒 Files selected for processing (1)
tests/integration/defs/perf/test_perf_sanity.py
fdf2e07 to
e31e491
Compare
…serve worker log on perf-sanity failures Disaggregated perf-sanity tests redirect each trtllm-serve (ctx/gen/disagg) subprocess's stdout+stderr into a file under the test output dir and never stream it to the pytest console. When a worker fails -- it crashes on its own, never becomes ready (timeout), or hangs -- the run_cmd finally blocks only terminate()/wait() the process, so its real error dies with the temporary slurm node; triage sees only "Test terminated unexpectedly" and empty stdout/stderr in the CI Report DB (NVBug 6388205). In each of the three server-owning finally blocks (AggrTestCmds.run_cmd and the ctx/gen and DISAGG_SERVER paths of DisaggTestCmds.run_cmd), when the test is failing (an exception is propagating) or the server exited abnormally on its own, echo the tail of the corresponding trtllm-serve.*.log via print_error (which also logs to general_logger -> Jenkins Raw Log -> CI Report DB stderr). The message states how the worker failed -- 'crashed on its own (returncode=N)' vs 'terminated by the harness after the test failed ... timeout or hang' -- plus the propagating failure reason, so all three modes are clear. Also initialize the disagg proc handles to None so those finally blocks no longer risk NameError. Signed-off-by: Shixiaowei02 <39303645+Shixiaowei02@users.noreply.github.com>
e31e491 to
4d1bc2a
Compare
|
/bot run --disable-fail-fast --stage-list "*GB200*PerfSanity*,*GB300*PerfSanity*" |
|
PR_Github #57165 Bot args parsing error: usage: /bot [-h] |
|
/bot -h |
GitHub Bot Help
Provide a user friendly way for developers to interact with a Jenkins server. Run See details below for each supported subcommand. Details
Launch build/test pipelines. All previously running jobs will be killed.
kill
Kill all running builds associated with pull request. skip
Skip testing for latest commit on pull request. reuse-pipeline
Reuse a previous pipeline to validate current commit. This action will also kill all currently running builds associated with the pull request. IMPORTANT NOTE: This is dangerous since lack of user care and validation can cause top of tree to break. |
|
/bot run --disable-fail-fast --stage-list "GB200PerfSanity*,GB300PerfSanity*" |
|
PR_Github #57170 [ run ] triggered by Bot. Commit: |
|
PR_Github #57170 [ run ] completed with state
|
Description
This pull request improves the reliability and diagnosability of the
perf_sanity.pyintegration test by ensuring that, when a server subprocess crashes or exits unexpectedly, the tail of its log file is echoed to the test output. This makes it much easier to diagnose failures in CI environments where logs might otherwise be lost. The changes also introduce utility functions to detect abnormal server exits and to print log tails, and update the server management logic to use these utilities.Enhanced error reporting and diagnostics:
_server_exited_abnormallyand_echo_server_log_tailhelper functions to detect unexpected server process exits and to print the last lines of server log files for easier debugging.run_cmdmethod to call these helpers when a server crashes or the test fails, ensuring logs are surfaced in CI outputs. This change is applied to all relevant server types (aggregated, disaggregated, benchmark). [1] [2] [3]Imports and code organization:
signal,sys) and updated the import ofprint_errorfor improved error output. [1] [2]Minor code quality improvements:
Nonebefore use to avoid potentialUnboundLocalError.Test Coverage
PR Checklist
Please review the following before submitting your PR:
PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.
PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.
Test cases are provided for new code paths (see test instructions)
If PR introduces API changes, an appropriate PR label is added - either
api-compatibleorapi-breaking. Forapi-breaking, includeBREAKINGin the PR title.Any new dependencies have been scanned for license and vulnerabilities
CODEOWNERS updated if ownership changes
Documentation updated as needed
Update tava architecture diagram if there is a significant design change in PR.
The reviewers assigned automatically/manually are appropriate for the PR.
Please check this after reviewing the above items as appropriate for this PR.
GitHub Bot Help
To see a list of available CI bot commands, please comment
/bot help.Summary by CodeRabbit